#pragma ident "$Id XY. CAO. 2016-11-24 $"

/**
 * @file MGEXDCBReader.cpp
 * Class to read DCB data from MGEX CAS/DLR.
 */

//============================================================================
//
//  Copyright (c) 
//
//  XY. CAO, Wuhan Uniersity, 2016 
//============================================================================


#include "StringUtils.hpp"
#include "MGEXDCBReader.hpp"
#include "YDSTime.hpp"

using namespace gpstk::StringUtils;

using namespace std;

namespace gpstk
{

	// constructor
	MGEXDCBReader::MGEXDCBReader()
	{
		version = 0.01;
		valid = 0;
		validEoH = false;

		satBiasData.clear();
		recBiasData.clear();
	}
   
      // Method to store load ocean tide harmonics data in this class'
      // data map
   void MGEXDCBReader::loadData()
      noexcept(false)
   {
      try
      {
            // a buffer
         string line;
		 
		 validEoH = false; valid = 0;
         
            // Now, let's read data
         while( !validEoH )
         {
			 // read first line 
			 formattedGetLine(line, true);
			 StringUtils::stripTrailing(line);
			 
			 if(line.size() == 0)
			 {
				 //FFStreamError e("No data read");
				 //GPSTK_THROW(e);
				 continue; // for the blank line of DLR
			 }

			 // the first line:%=BIA
			 if( line.substr( 0,5) == "%=BIA" )
			 {
				 version = asDouble(line.substr( 6,4));
				 agency = strip(line.substr( 11,3));
				 valid |= validVersion;
			 }
			 else if( line[0] == '*')
			 {
				 commentList.push_back(strip(line));
				 valid |= validComment;
			 }
			 else if( line == "+FILE/REFERENCE")
			 {
				 valid |= validReference;
				 // nothing to do
			 }
			 else if( line == "+FILE/COMMENT") // DLR-specified
			 {
				 // nothing to do
			 }
			 else if( line == "+INPUT/ACKNOWLEDGMENTS") // DLR-specified
			 {
				 // nothing to do
			 }
			 else if( line == "+BIAS/DESCRIPTION" )
			 {
				 valid |= validDescription;
				 // nothing to do
			 }
			 else if( line == "+BIAS/SOLUTION" )
			 {
				 valid |= validSolution;

				 // the bias data we want
				 while( 1 )
				 {
					 formattedGetLine(line, true);
					 StringUtils::stripTrailing(line);
					 if( line.find("-BIAS/SOLUTION") != string::npos )
					 {
						 break;
					 }

					 // the first line of bias solution
					 if( line[0] == '*')
					 {
						 commentList.push_back(strip(line));
					 }
					 else
					 {
						 // modified the read method because the format of DLR file is changed from 2016/092,
						 // The old method: according to the char position
						 // The new method: split line according to blank(fast)						 
						 std::vector<std::string> l = split(line,' ');

						 // check the bias type
						 // Warning: From 2016/183, DLR changed "DCB" to "DSB"
						 if( l[0] != "DCB" && l[0] != "DSB")
						 {
							 continue;
						 }
						 else
						 {
						 
							 // get the satID
							 SatID bsat;

							 std::string ssat = l[2]; 

							 if( ssat[0] == 'G' )
							 {
								 bsat.system = SatelliteSystem::GPS;
							 }
							 else if( ssat[0] == 'R' )
							 {
								 bsat.system = SatelliteSystem::GLONASS;
							 }
							 else if( ssat[0] == 'E' )
							 {
								 bsat.system = SatelliteSystem::Galileo;
							 }
							 else if( ssat[0] == 'C' )
							 {
								 bsat.system = SatelliteSystem::BDS;
							 }
							 else
							 {
								 continue;// other system
							 }

							 if( ssat.size() == 3 )
							 {
								 // not station
								 bsat.id = asInt(ssat.substr(1,2));
							 }

							 std::string ssite, bdomes,type12;
							 CommonTime btime;
							 bias bbias;TypeBias type_bias;
					 
							 if( l.size() == 10 ) // satellite 
							 {
								 // without site and domes
								 ssite = ""; bdomes = "";
								 type12 = l[3] + '-' + l[4];

								 std::vector<string> t = split(l[5],':');
								 int tempY = asInt(t[0]);
								 // For DLR after 2017
								 if( tempY < 2000 )
									 tempY += 2000;
								 YDSTime bstart( tempY, asInt(t[1]),0,TimeSystem::GPS );
								 btime = bstart.convertToCommonTime();

								 bbias.value = asDouble(l[8]);
								 bbias.std = asDouble(l[9]);

								 type_bias[type12] = bbias;

							 }
							 
							 else if( l.size() == 11 ) // receiver
							 {
								 // with site, without domes(DLR)
								 ssite = l[3];
								 /*
								 bdomes = "";
								 type12 = l[4] + '_' + l[5];

								 std::vector<string> t = split(l[6],':');
								 YDSTime bstart( asInt(t[0] ) + 2000, asInt(t[1]),0,TimeSystem::GPS );
								 btime = bstart.convertToCommonTime();

								 bbias.value = asDouble(l[9]);
								 bbias.std = asDouble(l[10]);

								 type_bias[type12] = bbias;
								 */
							 }
							 else if( l.size() == 12 ) // receiver
							 {
								 // with site and domes(CAS) 
								 ssite = l[3];
								 /*bdomes = l[4];
								 type12 = l[5] + '_' + l[6];

								 std::vector<string> t = split(l[7],':');
								 YDSTime bstart( asInt(t[0] ) + 2000, asInt(t[1]),0,TimeSystem::GPS );
								 btime = bstart.convertToCommonTime();

								 bbias.value = asDouble(l[10]);
								 bbias.std = asDouble(l[11]);

								 type_bias[type12] = bbias;
								 */
							 }
							 


							 // TODO: This loop method is very slow!
							 // add to map
							 if( ssite != "" )
							 {
								 /*
								 // receiver
								 rcvTypeBias rcv_type_bias;

								 rcv_type_bias[ bsystem ] = type_bias;

								 rcvSysTypeBias rcv_sys_type_bias;
								 rcv_sys_type_bias[ssite] = rcv_type_bias;

								 if( (recBiasData.size() == 0) || (recBiasData.find( btime ) == recBiasData.end() ) )
								 {
									 // time
									 recBiasData.insert( make_pair(btime,rcv_sys_type_bias) );
								 }
								 else
								 {
									 rcvSysTypeBias map_rcv_sys_type_bias;

									 map_rcv_sys_type_bias = recBiasData[btime];

									 if( map_rcv_sys_type_bias.find( ssite ) == map_rcv_sys_type_bias.end() )
									 {
										 // station ID string
										 map_rcv_sys_type_bias[ssite] = rcv_type_bias;
										 recBiasData[btime] = map_rcv_sys_type_bias;
									 }
									 else
									 {
										 rcvTypeBias map_rcv_type_bias;

										 map_rcv_type_bias = map_rcv_sys_type_bias[ssite];

										 if( map_rcv_type_bias.find(bsystem) == map_rcv_type_bias.end() )
										 {
											 // sat system
											 map_rcv_type_bias[bsystem] = type_bias;
											 map_rcv_sys_type_bias[ssite] = map_rcv_type_bias;
											 recBiasData[btime] = map_rcv_sys_type_bias;
										 }
										 else
										 {
											 TypeBias map_type_bias;

											 map_type_bias = map_rcv_type_bias[bsystem];

											 if( map_type_bias.find(type12) == map_type_bias.end() )
											 {
												 // TypeID
												 map_type_bias[type12] = bbias;
												 map_rcv_type_bias[bsystem] = map_type_bias;
												 map_rcv_sys_type_bias[ssite] = map_rcv_type_bias;
												 recBiasData[btime] = map_rcv_sys_type_bias;
											 }

										 }

									 }


								 }
								 */
							 }
							 else
							 {

								 // satellite
								 satTypeBias sat_type_bias;

								 sat_type_bias[ bsat ] = type_bias;

								 
								 if( (satBiasData.size() == 0) || (satBiasData.find( btime ) == satBiasData.end()) )
								 {
									 // time
									 satBiasData.insert( make_pair(btime,sat_type_bias) );
								 }
								 else
								 {
									 satTypeBias map_sat_type_bias;

									 map_sat_type_bias = satBiasData[btime];

									 if( map_sat_type_bias.find( bsat ) == map_sat_type_bias.end() )
									 {
										 // satID
										 map_sat_type_bias[bsat] = type_bias;
										 satBiasData[btime] = map_sat_type_bias;
									 }
									 else
									 {
										 TypeBias map_type_bias;

										 map_type_bias = map_sat_type_bias[bsat];

										 if(map_type_bias.find(type12) == map_type_bias.end() )
										 {
											 // typeID
											 map_type_bias[type12] = bbias;
											 map_sat_type_bias[bsat] = map_type_bias;
											 satBiasData[btime] = map_sat_type_bias;

										 }
									 }

								 }
							 
							 }

						 }
					 }

				 } // End while 1


			 }
			 else if( line.substr( 0,8) == "%=ENDBIA" )
			 {
				 validEoH = true;
			 }
			 else
			 {
				 // The format of sinex bias has not been determined. 
				 //FFStreamError e("Unidentified format in MGEX DCB file!");
				 //GPSTK_THROW(e);
			 }
             

         }  // End of while

////     for(std::map< CommonTime, satTypeBias >::iterator it = satBiasData.begin();
////         it != satBiasData.end();
////         ++it)
////     {
////         cout << "time:" << (*it).first << endl;
////         for(satTypeBias::iterator itstb = (*it).second.begin();
////             itstb != (*it).second.end();
////             ++itstb)
////         {
////             cout << "sat:" << (*itstb).first << endl;
////             for(TypeBias::iterator ittb = (*itstb).second.begin();
////                     ittb != (*itstb).second.end();
////                     ++ittb)
////             {
////                 cout << "type" << (*ittb).first << endl;
////                 cout << "value:" << (*ittb).second.value << endl;
////             }
////         }
////     }

		 if( (valid & allValid) != allValid )
		 {
			 FFStreamError e("Unknown file format");
			 GPSTK_THROW(e);
		 }
		 else
		 {
			 (*this).close();
		 }

      }  // End of try block
      catch (EndOfFile& e)
      {
		  cout << lineNumber << endl;
            // We should close this data stream before returning
         (*this).close();

         return;
      }
	  catch (Exception& e)
      {
		  cout << lineNumber << endl;
         // We should close this data stream before returning
         (*this).close();

         return;

      }


   }  // End of 'DCBDataReader::loadData()'



      // Method to open AND load DCB data file. 
   void MGEXDCBReader::open(const char* fn)
		noexcept(false)
   {

      // We need to be sure current data stream is closed
      (*this).close();

		try
		{
         // Open data stream
         FFTextStream::open(fn, std::ios::in);
         if( !FFTextStream::is_open() )
         {
            FileMissingException fm("The DCB file '" + std::string(fn) + "' doesn't exist " 
                                    + "or you don't have the reading permission");
            GPSTK_THROW(fm);
         }

		 // This step stores DCB
		 loadData();

		}
      catch(FileMissingException& e)
      {
         GPSTK_RETHROW(e);
      }

      return;

   }  // End of method 'DCBDataReader::open()'



      // Method to open AND load DCB data file. It doesn't
      // clear data previously loaded.
   void MGEXDCBReader::open(const string& fn)
		noexcept(false)
   {

      // We need to be sure current data stream is closed
      (*this).close();

	  try
	  {
		  // Open data stream
		  FFTextStream::open(fn.c_str(), std::ios::in);
		  if( !FFTextStream::is_open() )
		  {
			  FileMissingException fm("The DCB file '" + std::string(fn) + "' doesn't exist " 
				  + "or you don't have the reading permission");
			  GPSTK_THROW(fm);
		  }

		  // load DCB data
		  loadData();
	  }
	  catch(FileMissingException& e)
	  {
		  GPSTK_RETHROW(e);
	  }

      return;
   }  // End of method 'DCBDataReader::open()'



      // Get DCB data of a satellite
   MGEXDCBReader::bias MGEXDCBReader::getSatDCB(const CommonTime& time, 
                                                const SatID& sat, 
                                                const std::string & type12)
   {


	   bias satbias;
	   satbias.value = 0.0;satbias.std = 0.0;

	   // Only the daily dcb can be retrived!(DLR 7 day is also allowed to read)
	   CommonTime tempTime;
	   tempTime.set(time.getDays(),0,0,TimeSystem::GPS);

	   // Reverse Loop 
	   for(std::map< CommonTime, satTypeBias >::reverse_iterator it = satBiasData.rbegin();
		   it != satBiasData.rend();
		   ++it)
	   {
		   if( (tempTime >= it->first) ) // only older DCBs products are available
		   {
			   std::map< std::string, bias> mgexlist(satBiasData[tempTime][sat]);
			   if( mgexlist.find(type12) != mgexlist.end() )
			   {
				   satbias = mgexlist[type12];
				   return satbias; 
			   }
		   }

	   } // End for

	   
	   // This maybe cause some satellites lost.
	   DCBTypeNotFound e(" Not found DCB of this type");
	   GPSTK_THROW(e);


   }  // End of 'double MGEXDCBReader::getDCB(const string& station...'

   

}  // End of namespace gpstk

